package oop.ex1.processToolKit;

import oop.ex1.dataStructures.*;

import java.util.Date;
import java.util.Iterator;

/**
 * 
 * @author Elad Cagan and Avihay Asulin
 *
 */
public class Process implements ComparableObject {



	///////////////////////////////////////////////////////
	///////////////////////Constants///////////////////////
	///////////////////////////////////////////////////////

	/**
	 * 
	 */
	private final int DEFAULT_PRIORITY=0;

	// Constants for toString method
	private static final String OPEN_BRACKET = "{";
	private static final String CLOSING_BRACKET = "}";
	private static final String COMMA = ",";
	private static final String COLON = ":";
	private static final String SPACE_CHAR = " ";

	// Constants for compare method
	private static final int LARGER_THAN = 1;
	private static final int SMALLER_THAN = -1;
	private static final int EQUAL = 0;

	///////////////////////////////////////////////////////
	//////////////////////Data Members/////////////////////
	///////////////////////////////////////////////////////

	/**
	 * 
	 */
	private String _name;

	/**
	 * 
	 */
	private Process _parent;

	/**
	 * 
	 */
	private int _priority;

	/**
	 * 
	 */
	private LIFOComparableQueue _subProcessList;

	/**
	 * 
	 */
	private Date _terminationDate;

	
	///////////////////////////////////////////////////////
	/////////////////////Constructors//////////////////////
	///////////////////////////////////////////////////////
	
	/**
	 * 
	 * @param name
	 * @param parent
	 */
	public Process(String name,Process parent) {
		_name = name;
		_parent = parent;
		_priority = DEFAULT_PRIORITY;
		_subProcessList = new LIFOComparableQueue();
		if(parent!=null) { // If parents priority is bigger we will increase the child's priority
			if(_parent.getPriority()>DEFAULT_PRIORITY) {
				_priority = _parent.getPriority();
			}
			parent.pushSubProcess(this);
		}
	}
	
	/**
	 * 
	 */
	public Process(String name,Process parent,int priority) {
		_name = name;
		_parent = parent;
		_priority = priority;
		_subProcessList = new LIFOComparableQueue();
		if(parent!=null) { // If parents priority is bigger we will increase the child's priority
			if(_parent.getPriority()>priority) {
				_priority = _parent.getPriority();
			}
			parent.pushSubProcess(this);
		}
	}

	///////////////////////////////////////////////////////
	////////////ComparableObject Implementation////////////
	///////////////////////////////////////////////////////


	public int compare(ComparableObject other) {
		if(!this.equals(other)) { // Check first if the strings are equal
			// Match priorities first 
			if(this.getPriority()>((Process)other).getPriority()) {
				return LARGER_THAN;
			};
			if(this.getPriority()<((Process)other).getPriority()) {
				return SMALLER_THAN;
			};
			// Match process depths second
			if(this.findProcessDepth()>((Process)other).findProcessDepth()) {
				return LARGER_THAN;
			};
			if(this.findProcessDepth()<((Process)other).findProcessDepth()) {
				return SMALLER_THAN;
			}
			// Match lexicographically
			if(this._name.compareTo(((Process)other)._name)>0) {
				return LARGER_THAN;
			} else {
				return SMALLER_THAN;
			}
		}
		return EQUAL;
	}
	
	public boolean equals(ComparableObject other) {
		if(other instanceof Process) {
			return(((Process)other).getProcessName()==this.getProcessName()); 
		} 
		else {
			return false;
		}
	}


	///////////////////////////////////////////////////////
	/////////////////////Public methods////////////////////
	///////////////////////////////////////////////////////



	public int getPriority() {
		return _priority;
	}

	public void setPriority( int priority) {
		_priority = priority;
	}	




	/**
	 * 
	 */
	public void run() {
		final String RUNNING_PREFIX = "Running: ";

		System.out.println(RUNNING_PREFIX + this.toString());
		_terminationDate = new Date();
	}

	public String getProcessName() {
		return _name;
	}

	@SuppressWarnings("unchecked")
	public Iterator iterateSubProcesses() {
		return _subProcessList.LIFOIterator();
	}

	public Process pollSubProcess() throws NoMoreProcessesException {
		try {
			return (Process)_subProcessList.poll();
		} catch (EmptyQueueException e) {
			throw new NoMoreProcessesException();
		}
	}

	public void pushSubProcess(Process pushedProcess) {
		_subProcessList.push(pushedProcess);
	}


	/**
	 * Recursive method to print the processes according to specifications
	 */
	@SuppressWarnings("unchecked")
	public String toString() {

		String tempString = new String(); // Create a temporary string to contain the output

		tempString = tempString.concat(_name);
		if(!_subProcessList.isQueueEmpty()) { // Check if the process has no subprocesses
			tempString = tempString.concat(COLON).concat(SPACE_CHAR).concat(OPEN_BRACKET);
		}
		for(Iterator stringIterator = _subProcessList.LIFOIterator();stringIterator.hasNext();) {
			Process iteratedProcess = (Process)stringIterator.next(); // Iterating all subProcesses of this process
			tempString = tempString.concat(iteratedProcess.toString());
			if(!stringIterator.hasNext()) {
				tempString = tempString.concat(CLOSING_BRACKET);
			} 
			else {
				tempString = tempString.concat(COMMA);
			}
		}
		return tempString;	
	}
	
	
	///////////////////////////////////////////////////////
	///////////////////Package Methods/////////////////////
	///////////////////////////////////////////////////////
	
	protected boolean isTerminated() {
		return !(_terminationDate==null);
	}

	protected Process getParent(){
		return this._parent;
	}
	
	///////////////////////////////////////////////////////
	///////////////////Private Methods/////////////////////
	///////////////////////////////////////////////////////
	
	/**
	 * returns the distance from the root process where the root is represented by 0
	 * @return the distance from the root process
	 */
	private int findProcessDepth() {
		if(_parent==null) {
			return 0;
		} else {
			return 1 + _parent.findProcessDepth();
		}
	}
}
